#ifndef HANDLER_COMPOSE_HPP
#define HANDLER_COMPOSE_HPP
//
//  handlers.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2013-2014 Razvan Coca (razvan dot coca at  gmail dot com)
//
// chained handler as in http://stackoverflow.com/questions/18202398/is-there-any-way-to-asynchronously-wait-for-a-future-in-boost-asio
// also inspired from neat handler wrapping techniques in boost::asio library 
// and from boostcon session Bartosz Milewski: Haskell – The Pseudocode Language for C++ Template Metaprogramming
// http://isocpp.org/blog/2013/05/functional-patterns-in-c-bartosz-milewski
#include <utility>
#include <memory>
#include <vector>
#include <algorithm>
#include <atomic>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/scope_exit.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>

#include "command.hpp"
#include <cryptopp/cryptlib.h>
namespace patterns
{
namespace handlers
{
template<typename errtype>
int error_value(errtype& e)
{
    return int(e);
}
template<> int error_value(boost::system::error_code const &ec);
template<> int error_value(const CryptoPP::Exception& eec);


template<typename errtype>
bool is_error(errtype &e)
{
    return bool(e);
}
template<>
bool is_error(const CryptoPP::Exception& eec);

template<typename errtype>
bool not_found(errtype& ec, std::vector<int>&  skippable_list)
{

    if(skippable_list.cend()==std::find(skippable_list.cbegin(),skippable_list.cend(),error_value(ec)))
        return true;
    return false;
}


template<typename Handler1,typename Handler2>
class composed_handler
{
    Handler1  handler1_;
    Handler2  handler2_;
public:
    typedef void result_type ;

    composed_handler(Handler1&  handler1,Handler2&  handler2):
        handler1_(static_cast<Handler1&&>(handler1)),
        handler2_(handler2)
    {
    }

    template<typename ...Args>
    void operator()(Args... args)
    {
        handler1_(args...);
        handler2_(args...);
    }
    virtual ~composed_handler()
    {
    }
};
template<typename Handler1,typename Handler2>
composed_handler<Handler1, Handler2>
compose(Handler1&  h1, Handler2&  h2)
{
    return composed_handler<Handler1,Handler2>(h1,h2);
}



template<typename Handler1,typename Handler2>
class branch_composed_handler
{
    Handler1  handler1_;
    Handler2  handler2_;

    std::vector<int> skippable_list_;

public:

    typedef void result_type ;

    branch_composed_handler(Handler1&  handler1,Handler2&  handler2,
                            std::vector<int>&&  skippable_list
                           ):
        handler1_(handler1),
        handler2_(handler2),
        skippable_list_ (std::move(skippable_list))
    {
    }

    template<typename Arg0,typename ...Args>
    void operator()(Arg0 const& ec,Args... args)
    {

        handler1_(ec,args...);
        if(not_found(ec,skippable_list_))
            handler2_(ec,args...);
    }
    virtual ~branch_composed_handler()
    {
    }
};


template<typename Handler1,typename Handler2>
branch_composed_handler<Handler1,Handler2>
branch_compose(Handler1&  handler1,Handler2& handler2,std::vector<int>&&  skippable_list)
{
    return branch_composed_handler<Handler1,Handler2>(handler1,handler2,
            std::forward<std::vector<int>>(skippable_list));
}



template<typename Handler1,typename Handler2,typename Action>
class branch_composed_handler_command
{
    Handler1  handler1_;
    Handler2  handler2_;
    Action    op_;
    std::vector<int> skippable_list_;

public:
    typedef void result_type ;
    branch_composed_handler_command(Handler1&  handler1,
                                    Handler2&  handler2,
                                    Action& op,
                                    std::vector<int>&&  skippable_list):
        handler1_(handler1),
        handler2_(handler2),
        op_(op),
        skippable_list_ (std::move(skippable_list))
    {
    }

    template<typename Arg0,typename ...Args>
    void operator()(Arg0 const& ec,Args... args)
    {
        handler1_(ec,args...);

        if(not_found(ec,skippable_list_))
        {
            handler2_(ec,args...);
        }
        if(!is_error(ec))
        {
            patterns::command::invoke(op_);
        }

    }
    virtual ~branch_composed_handler_command()
    {
    }

};


template<typename Handler1,typename Handler2,typename Action>
branch_composed_handler_command<Handler1,Handler2,Action>
branch_compose(Handler1&  handler1,
               Handler2&  handler2,
               Action& op,
               std::vector<int>&&  skippable_list)
{
    return branch_composed_handler_command<Handler1,Handler2,Action>(
               handler1,
               handler2,
               op,
               std::forward<std::vector<int>>(skippable_list));
}





template<typename Handler1,typename Action>
class branch_handler_error_command
{
    Handler1  handler1_;
    Action    op_;
    std::vector<int> skippable_list_;

public:
    typedef void result_type ;
    branch_handler_error_command(Handler1&  handler1,
                                 Action& op,
                                 std::vector<int>&&  skippable_list):
        handler1_(static_cast<Handler1&&>(handler1)),
        op_(static_cast<Action&&>(op)),
        skippable_list_ (std::move(skippable_list))
    {
    }

    template<typename Arg0,typename ...Args>
    void operator()(Arg0 const& ec,Args... args)
    {

        handler1_(ec,args...);

        if(not_found(ec,skippable_list_))
        {
            patterns::command::invoke(op_);
        }

    }
    virtual ~branch_handler_error_command()
    {
    }

};


template<typename Handler1,typename Action>
branch_handler_error_command<Handler1,Action>
cleanup_branch_compose(Handler1&  handler1,
                       Action& op,
                       std::vector<int>&&  skippable_list)
{
    return branch_handler_error_command<Handler1,Action>(
               handler1,
               op,
               std::forward<std::vector<int>>(skippable_list));
}




} //end handlers
} //end patterns
#endif // HANDLER_COMPOSE_HPP
